package hello

import (
	"errors"
	"fmt"
	"math"
	"math/big"
	"strconv"
	"strings"
	"testing"
)

func readRNumber(s string) (any, error) {
	x, err := readRational(s)
	if err == nil {
		return x, nil
	}
	x, err = readReal(s)
	if err == nil {
		return x, nil
	}
	x, err = readInteger(s)
	if err == nil {
		return x, nil
	}
	x, err = readBigInteger(s)
	if err == nil {
		return x, nil
	}
	return 0, errors.New("no real number")
}
func readComplex(s string) (any, error) {
	realPart, imagPart := splitComplexParts(s)
	var r, i float64
	if realPart == "" {
		r = 0
	} else {
		x, err := readRNumber(realPart)
		if err != nil {
			return 0, err
		}
		r = numberIntoFloat64(x)
	}
	switch imagPart {
	case "+i":
		i = 1
	case "-i":
		i = -1
	case "":
		i = 0
	default:
		x, err := readRNumber(imagPart[:len(imagPart)-1])
		if err != nil {
			return 0, err
		}
		i = numberIntoFloat64(x)
	}

	return complex(r, i), nil
}

func numberIntoFloat64(x any) float64 {
	var r float64
	switch x := x.(type) {
	case *big.Rat:
		r, _ = x.Float64()
	case *big.Int:
		r, _ = x.Float64()
	case int64:
		r = float64(x)
	case float64:
		r = x
	default:
		return math.NaN()
	}
	return r
}

func splitComplexParts(s string) (realPart, imagePart string) {
	index := strings.LastIndex(s, "+")
	if index == -1 {
		index = strings.LastIndex(s, "-")
	}
	if index == -1 {
		return "", s
	}
	return s[:index], s[index:]
}

func readReal(s string) (any, error) {
	if !strings.Contains(s, ".") && !strings.Contains(s, "e") && !strings.Contains(s, "E") {
		return 0, errors.New("no fractional part")
	}
	return strconv.ParseFloat(s, 64)
}

func readRational(s string) (any, error) {
	if !strings.Contains(s, "/") {
		return 0, errors.New("not a/b pattern")
	}
	x := new(big.Rat)
	_, e := x.SetString(s)
	if !e {
		return x, errors.New("invalid rational format")
	}
	return x, nil
}

func readBigInteger(s string) (any, error) {
	x := new(big.Int)
	_, e := x.SetString(s, 10)
	if !e {
		return x, errors.New("invalid big integer format")
	}
	return x, nil
}

func readInteger(s string) (any, error) {
	return strconv.ParseInt(s, 10, 64)
}

func readNumber(s string) (any, error) {
	if strings.Contains(s, "i") {
		if s == "i" {
			return 0, errors.New("i is no number")
		}
		return readComplex(s)
	}
	return readRNumber(s)
}

type readFunc func(string) (any, error)

var readFuns = map[string]readFunc{
	"Complex   ": readComplex,
	"Real      ": readReal,
	"Rational  ": readRational,
	"BigInteger": readBigInteger,
	"Integer   ": readInteger,
}

func TestReadRealNumber(t *testing.T) {
	tests := []string{
		"1",
		"1.0",
		"1.0e1",
		"1.0e+1",
		"1.0e-1",
		"1/2",
		"1234567890123456789012345678901234567",
		"123/4567890123456789012345678901234567",
		"1.0/2",
		"1.0/2.0",
		"1.0e-1.2",
		"+1.2",
		"1.2+14567890123456789012345678901234567i",
		"-14567890123456789012345678901234567-1.2i",
		"+1.2+14567890123456789012345678901234567i",
		"1+i",
		"-i",
		"+i",
		"1/2+i",
		"i",
		"0ff",
		"0xff",
		"x",
	}
	for _, test := range tests {
		x, err := readNumber(test)
		if err != nil {
			fmt.Printf("read number(%s) failed\n", test)
		} else {
			fmt.Printf("%s:\t %v\n", test, x)
		}
	}
}

/*
func TestPositionNegative(t *testing.T) {
	tests := []string{
		//"1",
		//"1.1",
		//"1.0",
		//"1.0e-1",
		//"1+2i",
		//"1/2",
		//"123456789012345678901234567890i",
		//"123456789012345678901234567890",
		//"123456789",
		//"12345678.",
		//"1/2+0.3i",
		//"123456789012345678901234567890.123-123456789012345678901234567890i",
		//"1e5",
		"123456789012345678901234567890e-5",
		"123456789012345678901234567890g-5",
	}
	for _, tt := range tests {
		fmt.Printf("test: %v\n", tt)
		for name, f := range readFuns {
			fmt.Printf("%s:\t", name)
			x, err := f(tt)
			if err != nil {
				fmt.Println("error")
			} else {
				fmt.Printf("%v\n", x)
			}
		}
		fmt.Println()
	}
}
*/
